﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  EnhancedRemindersHelpers.cs
//    
// Abstract:
//
//  This class contains useful static common helper methods.
//  
//--

using System;
using System.Security;
using System.Diagnostics;
using System.DirectoryServices;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;

using Microsoft.OfficeCommunicationsServer.Applications.Common;

namespace Microsoft.EnhancedReminders
{
    /// <summary>
    /// Common static methods.
    /// </summary>
    public static class EnhancedRemindersHelpers
    {
        /// <summary>
        /// Initializes logging usng the specified source name.
        /// </summary>
        /// <param name="sourceName">Source name</param>
        /// <remarks>
        /// The initialization is dependent on the build type.
        /// </remarks>
        public static void InitializeLogging(string sourceName)
        {
            // Attach debugger, if so configured
            EnhancedRemindersHelpers.LaunchDebugger();

#if DEBUG
            // Enable diagnostic logging by default on debug builds
            TraceLogLevel logLevel = TraceLogLevel.All;
#else
            // Disable diagnostic logging by default on retail builds
            TraceLogLevel logLevel = TraceLogLevel.None;
#endif

            // Get the diagnostic logging level
            logLevel = (TraceLogLevel)Helpers.GetSetting<int>(Constants.TraceLogLevelSetting, (int)logLevel);

#if DEBUG
            // Initialize logging. Diagnostic logs always go to System.Diagnostics.Trace on debug builds.
            Logger.Initialize(sourceName, logLevel, true);
#else
            // Initialize logging. Diagnostic logs never go to System.Diagnostics.Trace on retail builds 
            // because of the potential performance hit.
            Logger.Initialize(sourceName, logLevel, false);
#endif
        }

        /// <summary>
        /// Attaches the debugger if requested in the applications exe.config.
        /// </summary>
        /// <remarks>
        /// This is meant to be used at service start-up to aid debugging of start-up issues.
        /// </remarks>
        private static void LaunchDebugger()
        {
            if (Helpers.GetSetting<bool>(Constants.LaunchDebuggerSetting, false))
            {
                Debugger.Launch();   
            }
        }

        /// <summary>
        /// Performs an Active Directory query to obtain the SIP address of a domain user based on their e-mail address.
        /// </summary>
        /// <param name="context">Logging context</param>
        /// <param name="emailAddress">The e-mail address of the user</param>
        /// <returns>The SIP address for the AD user with the given email address or null if a sip address could not be found.</returns>
        /// <remarks>Note that this is a synchronous operation and will cause a thread to block for a short amount of time.</remarks>
        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public static string ObtainSipAddress(Context context, string emailAddress)
        {
            Debug.Assert(context != null, "context != null");

            DirectorySearcher searcher = new DirectorySearcher();
            SearchResult adResult;
            string sipAddress = null;

            try
            {
                Logger.WriteTraceInformation(context, "Performing Active Directory query.");

                searcher.SizeLimit = 1;
                searcher.Filter = string.Format(CultureInfo.InvariantCulture, Constants.LdapEmailQuery, emailAddress);
                adResult = searcher.FindOne();

                if (adResult == null || adResult.Properties == null)
                {
                    Logger.WriteTraceError(context, "Active Directory lookup failed. Could not find user.");
                }
                else if (adResult.Properties[Constants.LdapSipEntry] == null || adResult.Properties[Constants.LdapSipEntry].Count == 0)
                {
                    Logger.WriteTraceError(context, "No SIP address found for user in Active Directory.");
                }
                else
                {
                    Logger.WriteTraceInformation(context, "SIP address found.");
                    sipAddress = adResult.Properties[Constants.LdapSipEntry][0].ToString();
                }
            }
            catch (COMException e)
            {
                Logger.WriteTraceError(context, e, "Could not perform Active Directory query.");
            }

            return sipAddress;
        }

        /// <summary>
        /// Serializes the specified reminder and encodes it as a string.
        /// </summary>
        /// <param name="context">Logging context</param>
        /// <param name="reminderItem">Reminder to be serialized</param>
        /// <remarks>
        /// This method is used to create the contents of a reminder extended property.
        /// </remarks>
        /// <returns>
        /// The serialized reminder.
        /// </returns>
        public static string SerializeReminderItem(Context context, ReminderItem reminderItem)
        {
            Debug.Assert(reminderItem != null, "reminderItem != null");

            byte[] content = null;

            using (MemoryStream stream = new MemoryStream())
            {
                BinaryFormatter binaryFormatter = new BinaryFormatter();

                try
                {
                    binaryFormatter.Serialize(stream, reminderItem);
                }
                catch (SecurityException ex)
                {
                    // The reminder has the wrong format
                    Logger.WriteTraceWarning(context, ex, "Failed to serialize reminder.");
                    return null;
                }
                catch (SerializationException ex)
                {
                    // The reminder has the wrong format
                    Logger.WriteTraceWarning(context, ex, "Failed to serialize reminder.");
                    return null;
                }

                stream.Seek(0, System.IO.SeekOrigin.Begin);
                content = new byte[stream.Length];
                int bytesRead = stream.Read(content, 0, (int)stream.Length);
                Debug.Assert(bytesRead == (int)stream.Length, "bytesRead == (int)stream.Length");
            }

            Debug.Assert(content != null, "content != null");

            return Convert.ToBase64String(content);
        }

        /// <summary>
        /// De-serializes the specified encoded, serialized reminder.
        /// </summary>
        /// <param name="context">Logging context</param>
        /// <param name="encodedReminderItem">Encoded, serialized reminder</param>
        /// <remarks>
        /// This method is used to extract the contents of a reminder extended property.
        /// </remarks>
        /// <returns>
        /// The reminder, or null if unsuccessful.
        /// </returns>
        public static ReminderItem DeserializeReminderItem(Context context, string encodedReminderItem)
        {
            byte[] reminderContent = null;

            try
            {
                // Decode serialized reminder from the specified string
                reminderContent = Convert.FromBase64String(encodedReminderItem);
            }
            catch (FormatException ex)
            {
                // The reminder has an invalid format
                Logger.WriteTraceWarning(context, ex, "Failed to de-code the reminder.");
                return null;
            }

            if (reminderContent == null)
            {
                // The reminder has an invalid format
                Logger.WriteTraceWarning(context, "Failed to de-code the reminder.");
                return null;
            }

            MemoryStream stream = new MemoryStream(reminderContent);
            BinaryFormatter binaryFormatter = new BinaryFormatter();

            ReminderItem reminderItem = null;

            try
            {
                // De-serialize the ReminderItem object
                reminderItem = binaryFormatter.Deserialize(stream) as ReminderItem;
            }
            catch (SecurityException ex)
            {
                // The reminder has the wrong format
                Logger.WriteTraceWarning(context, ex, "Failed to de-serialize reminder.");
                return null;
            }
            catch (System.Runtime.Serialization.SerializationException ex)
            {
                // The reminder has the wrong format
                Logger.WriteTraceWarning(context, ex, "Failed to de-serialize reminder.");
                return null;
            }
            catch (FileLoadException ex)
            {
                // The version of the common assembly is different from that used to create the reminder item
                Logger.WriteTraceWarning(context, ex, "Failed to de-serialize reminder.");
                return null;
            }

            if (reminderItem == null)
            {
                // The content of the reminder property is of the wrong type
                Logger.WriteTraceWarning(context, "The content of the reminder property is of the wrong type.");
                return null;
            }

            return reminderItem;
        }
    }
}
